package org.blim.blackdawn.shader;

import java.nio.Buffer;

import org.blim.blackdawn.R;
import org.blim.blackdawn.model.TriangleArray;
import org.blim.blackdawn.resource.ResourceUtils;

import android.content.Context;
import android.opengl.GLES20;
import android.opengl.Matrix;
import android.util.Log;

public class DiffuseArrayShader implements Shader<TriangleArray> {

	private static final String TAG = DiffuseArrayShader.class.getSimpleName();
	
	/** Pre-allocate storage for the final combined matrix. */
	private float[] mMVPMatrix = new float[16];
	
	private int mProgramHandle;
	
	/** This will be used to pass in the texture. */
	private int mTextureUniformHandle;
	
	/** This will be used to pass in the transformation matrix. */
	private int mMVPMatrixHandle;

	/** This will be used to pass in the modelview matrix. */
	private int mMVMatrixHandle;
	
	/** This will be used to pass in model position information. */
	private int mVertexHandle;

	/** This will be used to pass in model color information. */
	private int mColorHandle;

	/** This will be used to pass in model normal information. */
	private int mNormalHandle;

	/** This will be used to pass in the light position. */
	private int mLightPosHandle;

	/** This will be used to pass in the light color. */
	private int mLightColHandle;

	/** This will be used to pass in the light count. */
	private int mLightCountHandle;

	/** This will be used to pass in model texture coordinate information. */
	private int mTextureCoordinateHandle;

	public DiffuseArrayShader(Context context) {
		try {
			String vertexShader = ResourceUtils.readRawResource(
					context,
					R.raw.diffuse_vertex_shader);

			String fragmentShader = ResourceUtils.readRawResource(
					context,
					R.raw.diffuse_fragment_shader);
			
			final int vertexShaderHandle = 
					Shaders.compileShader(GLES20.GL_VERTEX_SHADER, vertexShader);
			final int fragmentShaderHandle = 
					Shaders.compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShader);

			mProgramHandle = Shaders.createAndLinkProgram(
					vertexShaderHandle,
					fragmentShaderHandle, 
					new String[] { "a_Position", "a_Color", "a_Normal", "a_TexCoordinate" });
		} catch (Exception e) {
			Log.e(TAG, "Failed to load shaders", e);
		}
	}

	@Override
	public void draw(
			TriangleArray model,
			float[] modelMatrix,
			float[] view,
			float[] projection,
			float[] lightPositions,
			float[] lightColors,
			int lightCount) {
		
		// Set our per-vertex lighting program.
		GLES20.glUseProgram(mProgramHandle);

		// Set the active texture unit to texture unit 0.
		GLES20.glActiveTexture(GLES20.GL_TEXTURE0);

		// Bind the texture to this unit.
		GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, model.mTextureHandle);

		// Tell the texture uniform sampler to use this texture in the shader by
		// binding to texture unit 0.
		GLES20.glUniform1i(mTextureUniformHandle, 0);
		
		// Set program handles for cube drawing.
		mMVPMatrixHandle = 
				GLES20.glGetUniformLocation(mProgramHandle, "u_MVPMatrix");
		mMVMatrixHandle = 
				GLES20.glGetUniformLocation(mProgramHandle, "u_MVMatrix");
		mLightPosHandle = 
				GLES20.glGetUniformLocation(mProgramHandle, "u_LightPos");
		mLightColHandle = 
				GLES20.glGetUniformLocation(mProgramHandle, "u_LightCol");
		mLightCountHandle = 
				GLES20.glGetUniformLocation(mProgramHandle, "u_LightCount");
		mTextureUniformHandle = 
				GLES20.glGetUniformLocation(mProgramHandle, "u_Texture");
		mVertexHandle = 
				GLES20.glGetAttribLocation(mProgramHandle, "a_Position");
		mColorHandle = 
				GLES20.glGetAttribLocation(mProgramHandle, "a_Color");
		mNormalHandle = 
				GLES20.glGetAttribLocation(mProgramHandle, "a_Normal");
		mTextureCoordinateHandle = 
				GLES20.glGetAttribLocation(mProgramHandle, "a_TexCoordinate");
		
		// Pass in the position information
		Buffer vertices = model.mVertices;
		vertices.position(0);
		GLES20.glVertexAttribPointer(mVertexHandle, model.mVertexSize,
				GLES20.GL_FLOAT, false, 0, vertices);

		GLES20.glEnableVertexAttribArray(mVertexHandle);

		// Pass in the color information
		Buffer colors = model.mColors;
		colors.position(0);
		GLES20.glVertexAttribPointer(mColorHandle, model.mColorSize,
				GLES20.GL_FLOAT, false, 0, colors);

		GLES20.glEnableVertexAttribArray(mColorHandle);

		// Pass in the normal information
		Buffer normals = model.mNormals;
		normals.position(0);
		GLES20.glVertexAttribPointer(mNormalHandle, model.mNormalSize,
				GLES20.GL_FLOAT, false, 0, normals);

		GLES20.glEnableVertexAttribArray(mNormalHandle);

		// Pass in the texture coordinate information
		Buffer textureCoordinates = model.mTextureCoordinates;
		textureCoordinates.position(0);
		GLES20.glVertexAttribPointer(mTextureCoordinateHandle,
				model.mTextureCoordinateSize, GLES20.GL_FLOAT, false, 0,
				textureCoordinates);

		GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle);

		// This multiplies the view matrix by the model matrix, and stores the
		// result in the MVP matrix
		// (which currently contains model * view).
		Matrix.multiplyMM(mMVPMatrix, 0, view, 0, modelMatrix, 0);

		// Pass in the modelview matrix.
		GLES20.glUniformMatrix4fv(mMVMatrixHandle, 1, false, mMVPMatrix, 0);

		// This multiplies the modelview matrix by the projection matrix, and
		// stores the result in the MVP matrix
		// (which now contains model * view * projection).
		Matrix.multiplyMM(mMVPMatrix, 0, projection, 0, mMVPMatrix, 0);

		// Pass in the combined matrix.
		GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0);

		// Pass in the light position in eye space.
		GLES20.glUniform4fv(mLightPosHandle, lightCount, lightPositions, 0);

		// Pass in the light color.
		GLES20.glUniform4fv(mLightColHandle, lightCount, lightColors, 0);

		// Pass in the light count.
		GLES20.glUniform1i(mLightCountHandle, lightCount);
		
		// Draw the array.
		GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, model.mVertexCount);
	}
	
}
